use std::str::Chars;

use crate::Syntax::{text::TextSpan, token::SyntaxToken};

use super::{text::SourceLocation, token::SyntaxKind};

#[allow(non_snake_case)]
pub struct LexicalScanner<'a> {
    sourceLength: usize,
    sourceChars: Chars<'a>,
    inputStr: &'a str,
    current: SourceLocation,
}

#[allow(non_snake_case)]
impl<'a> LexicalScanner<'a> {
    pub fn new(input: &'a str) -> LexicalScanner<'a> {
        LexicalScanner {
            sourceLength: input.len(),
            sourceChars: input.chars(),
            inputStr: input,
            current: SourceLocation::new(1, 0),
        }
    }
    // 取一个字符,如果已经取完了字节返回字符0;
    fn peek(&self) -> char {
        self.sourceChars.clone().next().unwrap_or('\0')
    }
    // 判断是否结束;
    fn isEof(&self) -> bool {
        self.sourceChars.as_str().is_empty()
    }

    fn next(&mut self) -> Option<char> {
        let next_char = self.sourceChars.next();
        // 用来计算字符位置;
        self.current.column += 1;

        if next_char.unwrap_or('\0').eq(&'\n') {
            self.current.line += 1;
            self.current.column = 0;
        }

        next_char
    }
    fn currentPosition(&self) -> usize {
        self.sourceLength - self.sourceChars.as_str().len()
    }

    fn skipWhile(&mut self, mut fn_while: impl FnMut(char) -> bool) {
        while fn_while(self.peek()) && !self.isEof() {
            self.next();
        }
    }

    fn makeTextSpan(&self, start: usize, end: usize) -> TextSpan {
        TextSpan::new(start, end - start, self.inputStr[start..end].to_string())
    }

    pub fn nextToken(&mut self) -> Option<SyntaxToken> {
        // 保存位置;
        let cur_loc = self.current.clone();

        //self.backChars = self.codeChars.count();

        let token_st = self.currentPosition();

        //print!("{:?}", cur_loc);
        //获取一个字符;
        let cur_char = match self.next() {
            Some(char) => char,

            None => {
                return Some(SyntaxToken::new(
                    SyntaxKind::Eof,
                    TextSpan::new(
                        token_st,
                        self.currentPosition() - token_st,
                        "\0".to_string(),
                    ),
                    cur_loc,
                ))
            }
        };

        //print!("Current:'{:?}'\t",cur_char);

        let kind = match cur_char {
            '+' => SyntaxKind::Plus,
            '-' => SyntaxKind::Minus,
            '*' => SyntaxKind::Asterisk,
            '%' => SyntaxKind::Percent,

            '.' => SyntaxKind::Dot,
            ';' => SyntaxKind::Semicolon,
            '~' => SyntaxKind::Tilde,
            ':' => SyntaxKind::Colon,
            ',' => SyntaxKind::Comma,

            '(' => SyntaxKind::OpenParenthesis,
            ')' => SyntaxKind::CloseParenthesis,

            '[' => SyntaxKind::OpenBracket,
            ']' => SyntaxKind::CloseBracket,

            '{' => SyntaxKind::OpenBrace,
            '}' => SyntaxKind::CloseBrace,

            // 跳过空白符;
            c if c.is_whitespace() => {
                self.skipWhile(|x| x.is_whitespace());

                SyntaxKind::Whitespace
            }
            // 跳过注释;
            '/' => match self.peek() {
                '/' => {
                    self.skipWhile(|x: char| x.ne(&'\n'));
                    self.next();

                    SyntaxKind::Comments
                }
                '*' => {
                    let mut done = false;
                    while let Some(ch) = self.next() {
                        if ch.eq(&'*') && self.peek().eq(&'/') {
                            done = true;
                            break;
                        }
                    }
                    self.next();

                    if !done {
                        eprintln!(
                            "[Lexer] BlockComment: Unable to match the ending symbol( '*/' )!"
                        );
                        return None;
                    }

                    SyntaxKind::Comments
                }
                _ => SyntaxKind::Slash,
            },
            '=' => {
                if self.peek().eq(&'=') {
                    self.next();
                    SyntaxKind::Equals
                } else {
                    SyntaxKind::Assignment
                }
            }
            // 条件判断;
            '>' => {
                if self.peek().eq(&'=') {
                    self.next();

                    SyntaxKind::GreaterEquals
                } else {
                    SyntaxKind::Greater
                }
            }
            '<' => {
                if self.peek().eq(&'=') {
                    self.next();
                    SyntaxKind::LessEquals
                } else {
                    SyntaxKind::Less
                }
            }
            '|' => {
                if self.peek().eq(&'|') {
                    self.next();

                    SyntaxKind::Or
                } else {
                    SyntaxKind::BitOr
                }
            }
            '&' => {
                if self.peek().eq(&'&') {
                    self.next();
                    SyntaxKind::And
                } else {
                    SyntaxKind::BitAnd
                }
            }
            '!' => {
                if self.peek().eq(&'=') {
                    self.next();
                    SyntaxKind::BangEquals
                } else {
                    SyntaxKind::Bang
                }
            }
            // 字符串;
            '"' => {
                self.skipWhile(|s| s.ne(&'"'));
                self.next();

                SyntaxKind::String
            }
            // 字符;
            '\'' => {
                self.skipWhile(|x| x.ne(&'\''));
                self.next();

                SyntaxKind::Char
            }
            //数值;
            c @ '0'..='9' => {
                self.skipWhile(|x| x.is_ascii_digit());

                SyntaxKind::Number
            }
            // 描述符;
            //c if matches!(c, 'a'..='z' | 'A'..='Z' | '_') => {
            'a'..='z' | 'A'..='Z' | '_' => {
                self.skipWhile(|x| matches!(x,'a'..='z' | 'A'..='Z' | '0'..='9'| '_'));

                let id = &self.inputStr[token_st..self.currentPosition()];

                self.checkKeyword(id)
            }
            _ => {
                self.next();

                SyntaxKind::Unknown
            }
        };

        Some(SyntaxToken::new(
            kind,
            self.makeTextSpan(token_st, self.currentPosition()),
            cur_loc,
        ))
    }

    fn checkKeyword(&mut self, id: &str) -> SyntaxKind {
        match id {
            "or" => SyntaxKind::Or,
            "and" => SyntaxKind::And,
            "fun" => SyntaxKind::Func,
            "while" => SyntaxKind::While,
            "if" => SyntaxKind::If,
            "for" => SyntaxKind::For,
            "var" => SyntaxKind::Var,
            "break" => SyntaxKind::Break,
            "else" => SyntaxKind::Else,
            "return" => SyntaxKind::Return,
            _ => SyntaxKind::Identifier,
        }
    }
}
